package com.jxpanda.commons.base;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.jxpanda.commons.constant.StringConstant;
import com.jxpanda.commons.toolkit.*;
import com.jxpanda.commons.toolkit.json.JsonKit;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Panda
 */
@Slf4j
public class ServiceImpl<M extends Mapper<T>, T extends Entity> extends com.baomidou.mybatisplus.extension.service.impl.ServiceImpl<M, T> implements Service<T> {

    private final String tableName = getTableName(entityClass);

    private static final List<String> NEED_NOT_FILL = Stream.of("createdDate", "updatedDate", "deletedDate").collect(Collectors.toList());

    private static final String ID_SUFFIX = "id";

    @Override
    public boolean save(T entity) {
        return this.save(entity, StringConstant.BLANK);
    }

    @Override
    public boolean updateById(T entity) {
        return this.updateById(entity, StringConstant.BLANK);
    }

    @Override
    public boolean saveOrUpdate(T entity) {
        return this.saveOrUpdate(entity, StringConstant.BLANK);
    }


    @Override
    public boolean save(T entity, String executorId) {
        fillDefaultValue(entity, executorId);
        return super.save(entity);
    }

    @Override
    public boolean updateById(T entity, String executorId) {
        // 填充默认值
        fillDefaultValue(entity, executorId).saveClear();
        return super.updateById(entity);
    }

    @Override
    public boolean saveOrUpdate(T entity, String executorId) {
        // 填充默认值
        fillDefaultValue(entity, executorId).saveClear();
        // id有效 且 数据库中物理上存在数据才更新
        return (IdentifierKit.isIdEffective(entity.getId()) && baseMapper.isPhysicalExisting(tableName, entity.getId())) ?
                super.updateById(entity) : super.save(entity);
    }

    @Override
    public boolean saveBatch(Collection<T> entityList) {
        return saveBatch(entityList, StringConstant.BLANK);
    }

    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList) {
        return saveOrUpdateBatch(entityList, StringConstant.BLANK);
    }

    @Override
    public boolean saveBatch(Collection<T> entityList, String executorId) {
        return super.saveBatch(entityList.stream()
                .peek(entity -> {
                    // 填充默认值
                    fillDefaultValue(entity, executorId).saveClear();
                })
                .collect(Collectors.toList()));
    }

    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList, String executorId) {
        return super.saveOrUpdateBatch(entityList.stream()
                .peek(entity -> {
                    // 填充默认值
                    fillDefaultValue(entity, executorId).saveClear();
                })
                .collect(Collectors.toList()));
    }


    @Override
    public boolean remove(String executorId, UpdateWrapper<T> updateWrapper) {
        return super.update(updateWrapper
                .set(Entity.UPDATER_ID, executorId)
                .set(Entity.DELETED_DATE, LocalDateTime.now()));
    }

    @Override
    public boolean removeById(Serializable id, String executorId) {
        return super.update(new UpdateWrapper<T>()
                .set(Entity.UPDATER_ID, executorId)
                .set(Entity.DELETED_DATE, LocalDateTime.now())
                .eq(Entity.ID, id));
    }

    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList, String executorId) {
        return super.update(new UpdateWrapper<T>()
                .set(Entity.UPDATER_ID, executorId)
                .set(Entity.DELETED_DATE, LocalDateTime.now())
                .in(Entity.ID, idList));
    }

    @Override
    public boolean destroy(Wrapper<T> wrapper) {
        return baseMapper.destroy(tableName, wrapper);
    }

    @Override
    public boolean destroyById(Serializable id) {
        return baseMapper.destroyById(tableName, id);
    }

    @Override
    public boolean destroyByIds(Collection<? extends Serializable> ids) {
        return baseMapper.destroyByIds(tableName, ids);
    }

    @Override
    public boolean isExisting(Serializable id) {
        return count(new QueryWrapper<T>().eq(Entity.ID, id)) > 0;
    }

    @Override
    public boolean isExisting(Wrapper<T> wrapper) {
        return count(wrapper) > 0;
    }

    @Override
    public T getById(Serializable id) {
        T t = super.getById(id);
        return t == null ? emptyEntity() : t;
    }

    /**
     * 重写getOne函数，使用分页查询返回结果
     */
    @Override
    public T getOne(Wrapper<T> wrapper) {
        // 切换为使用分页查询，只查询1条数据，可以排除TooManyResultsException错误
        // 同时可以避免出现全表扫描
        return page(new Pagination<T>(1, 1).setSearchCount(false), wrapper)
                .getRecords()
                .stream()
                .findFirst()
                .orElse(emptyEntity());
    }

    /**
     * 重写listByIds函数
     * 针对入参做空安全处理
     */
    @Override
    public List<T> listByIds(Collection<? extends Serializable> ids) {
        if (ids == null || ids.isEmpty()) {
            return new ArrayList<>();
        }
        return super.listByIds(ids);
    }

    @Override
    public List<T> list(Wrapper<T> queryWrapper) {
        List<T> list = super.list(queryWrapper);
        return list == null ? new ArrayList<>() : list;
    }

    @Override
    public Map<? extends Serializable, T> associateById(Collection<? extends Serializable> ids) {
        return listByIds(ids).stream().collect(Collectors.toMap(Entity::getId, Function.identity()));
    }

    @Override
    public <K> Map<K, List<T>> groupBy(Function<? super T, ? extends K> keySelector, Wrapper<T> wrapper) {
        return list(wrapper).stream().collect(Collectors.groupingBy(keySelector));
    }

    @Override
    public <K> Map<K, T> associateBy(Function<? super T, ? extends K> keySelector, Wrapper<T> wrapper) {
        return associateBy(keySelector, (t1, t2) -> {
            log.info("[ASSOCIATE] duplicate value\n value1 = " + JsonKit.toJson(t1) + "\n" + "value2 = " + JsonKit.toJson(t2));
            return t1;
        }, wrapper);
    }

    @Override
    public <K> Map<K, T> associateBy(Function<? super T, ? extends K> keySelector, BinaryOperator<T> valueSelector, Wrapper<T> wrapper) {
        return list(wrapper).stream().collect(Collectors.toMap(keySelector, Function.identity(), valueSelector));
    }

    @Override
    public BigDecimal sum(String field, Wrapper<T> wrapper) {
        return baseMapper.sum(tableName, field, wrapper);
    }

    @Override
    public Pagination<T> pagination(Seeker<T> seeker) {
        return page(seeker.getPagination(), seeker.buildWrapper(entityClass));
    }

    @Override
    public List<T> listBySeeker(Seeker<T> seeker) {
        return list(seeker.buildWrapper(entityClass));
    }

    @Override
    public T getBySeeker(Seeker<T> seeker) {
        return getOne(seeker.buildWrapper(entityClass));
    }

    /**
     * 获取表名
     * 默认从@TableName注解上面取
     * 如果注解上没有配置，则把对象名称变为snake_case命名规范，尝试使用snake_case作为表名
     * 如果数据库报错，应该检查数据库的命名规范
     * 按照开发规约来的话，一般是不会报错的
     */
    private static <T> String getTableName(Class<T> clazz) {
        TableName annotation = clazz.getAnnotation(TableName.class);
        if (annotation == null || annotation.value().isBlank()) {
            return StringKit.snakeCase(clazz.getSimpleName());
        } else {
            return annotation.value();
        }
    }

    @SneakyThrows
    private T emptyEntity() {
        return Objects.requireNonNull(entityClass).getConstructor().newInstance();
    }


    /**
     * 填充默认值
     * 只有在save也就是insert操作的时候需要用
     * 判断逻辑如下
     */
    private T fillDefaultValue(T entity) {
        return fillDefaultValue(entity, null);
    }

    /**
     * 填充默认值
     * 只有在save也就是insert操作的时候需要用
     * 判断逻辑如下
     */
    private T fillDefaultValue(T entity, String executorId) {

        // 填充创建者Id
        if (IdentifierKit.isIdEffective(executorId)) {
            entity.setExecutor(executorId);
        }

        // 基于id是否有效来判断对象是做插入操作还是更新操作
        // 有id的话说明是更新，不操作对象了
        if (IdentifierKit.isIdNotEffective(entity.getId())) {
            ReflectionKit.getFieldMap(getEntityClass()).forEach((fieldName, field) -> {
                Object fieldValue = ReflectionKit.getFieldValue(entity, field);
                // ID在数据库都是用BigInt来存储的，是一个数字，如果传递空字符串，会报错
                // 但是传递null不会，因为会使用数据的默认值
                // 这里主动做一个容灾，如果传递了空字符串进来，把值设置为null，交由数据库默认值处理
                if (fieldName.toLowerCase().endsWith(ID_SUFFIX)) {
                    if (ObjectKit.isEmpty(fieldValue)) {
                        ReflectionKit.setFieldValue(entity, field, null);
                    }
                } else if (fieldValue == null && !NEED_NOT_FILL.contains(fieldName)) {
                    // 如果字段是空的，给字段一个默认值
                    ReflectionKit.initField(entity, field);
                }
            });
        }

        return entity;
    }

}
